/* Class to make a connection with a client. It will set up a connection
 * with the port that is supplied, and then set up a new connection with a 
 * random port. This second connection will use a new thread, 
 * to make multiple connections possible.
 */
import java.util.*;
import java.net.*;
import java.util.concurrent.*;

public class Server implements Runnable{
	ServerSocket serverSocket;
	private ServerStatus serverStatus;
	private Thread connectionChecker;
	int connectionport;

	private boolean serverRunning = false;
	public ArrayList<Thread> threads;
	public ArrayList<GameThreadStatus> sharedStatusArray;

	public Server(int port, ServerStatus serverStatus){
		this.connectionport = port;
		this.serverStatus = serverStatus;
		threads = new ArrayList<Thread>();
		sharedStatusArray = new ArrayList<GameThreadStatus>();
		this.connectionChecker = new Thread(new ConnectionChecker(this));
	}

	/* This function runs whenever the server is started, it will run for
	 * infinite time, until the server is stopped. The socket itself will 
	 * only run when serverStatus.running is true. Otherwise the socket
	 * will close itself.
	 */
	public void run(){
		try{
			serverSocket = new ServerSocket(connectionport);
			getConnection();
			serverSocket.setSoTimeout(5000);
			connectionChecker.start();
		} catch(java.io.IOException e){
		}

		try{
			while(true){
				if(!serverStatus.running){
					if(serverRunning){
						stop();
					} else {
						Thread.sleep(5000);
					}
				} else {
					if(serverRunning){
						Thread.sleep(5000);
					}else {
						getConnection();
						serverRunning = true;
					}
				}
				this.serverStatus.threadsOnline = threads.size();
			}
		} catch(Exception e){
			e.printStackTrace();
		}
	}
		

	private void getConnection(){
		Socket socket;
		//System.out.println("ServerStatus.running: " + this.serverStatus.threadsOnline + " threads.");
		while(serverStatus.running){
			try{
				socket = serverSocket.accept();
				System.out.println("Connection Recieved");
				this.serverStatus.threadsOnline++;
				startThread(socket);
			} catch(java.io.IOException i){
				//System.out.println(i);
			}
		}
	}
	
	/* Method that checks the array with sockets for connections that need 
	 * to be removed, and then kills those that do
	 */
	public void checkForDeadConnections(){
		for(int i = 0; i<sharedStatusArray.size(); i++){
			boolean living = sharedStatusArray.get(i).living;
			if(!living){
				killprocess(i);
			}
		}

		//debugging
		System.out.print("\n");
		System.out.print("Living Threads: ");
		for(int i = 0; i<sharedStatusArray.size(); i++){
		System.out.print(i);
		}
		System.out.print("\n");

	}

	/* Method that removes a process from memory.
	 */
	public void killprocess(int id){
		threads.remove(id);		
		sharedStatusArray.remove(id); 
		serverStatus.threadsOnline--;	
	// sets the id's to work with the array.
		for(int i = 0; i<threads.size(); i++){
			System.out.println("Killing Thread " + i);
			sharedStatusArray.get(i).setID(i);
		}	
	}

	/* Method that starts a new Thread for an incoming connection.
	 */
	private void startThread(Socket socket){
		GameThreadStatus statusObject = new GameThreadStatus(serverStatus.threadsOnline);
		Thread clientThread = new Thread(new GameServerThread(socket, statusObject));
		threads.add(clientThread);
		threads.get(threads.size() -1).start();
		sharedStatusArray.add(statusObject);
	}
	
	/* Method that stops the server
	 */
	private void stop(){
		try{
			serverSocket.close();
			for(int i = 0; i<threads.size(); i++){
				killprocess(i);
			}
			threads = new ArrayList<Thread>();
			sharedStatusArray = new ArrayList<GameThreadStatus>();
			serverRunning = false;
		} catch(java.io.IOException i){
			System.out.println(i);
		}
	}
}
